#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <opus/opus.h>
#include <ogg/ogg.h>
#include <time.h>

// 核心配置（严格匹配规范，避免动态参数）
#define SAMPLE_RATE     48000   // 强制支持的采样率
#define CHANNELS        1       // 单声道（兼容性最佳）
#define DURATION        20       // 20秒音频（总采样数960000）
#define FREQUENCY       1000    // 1KHz纯音
#define AMPLITUDE       8000    // 16位PCM安全振幅
#define FRAME_SIZE      960     // 固定20ms帧长（48000*0.02）
#define BITRATE         32000   // 固定比特率（避免VBR波动）
#define MAX_OPUS_FRAME  1024    // 足够容纳32kbps下20ms帧
#define TOTAL_SAMPLES   (SAMPLE_RATE * DURATION)  // 总采样数（确保时长准确）

// Ogg/Opus头结构（1字节对齐，无内存错位）
#pragma pack(push, 1)
typedef struct {
    char        magic[8];       // "OpusHead"（大小写严格） // 4f 70 75 73  48 65 61 64  
    uint8_t     version;        // 仅版本1
    uint8_t     channels;       // 声道数
    uint16_t    pre_skip;       // 预跳过0（小端）
    uint32_t    sample_rate;    // 48000（小端）
    uint16_t    gain;           // 0dB（小端）
    uint8_t     channel_map;    // 0=默认单声道
} OpusHead;  // 固定19字节

typedef struct {
    char        magic[8];       // "OpusTags"（大小写严格） // 4f 70 75 73 54 61 67 73  
    uint32_t    vendor_len;     // 厂商长度0（小端）
} OpusTag;  // 固定12字节
#pragma pack(pop)

// Ogg容器状态
typedef struct {
    ogg_stream_state os;
    ogg_page         og;
    ogg_packet       op;
} OggState;

// 小端转换（兼容所有平台）
static uint16_t le16(uint16_t val) 
{
    return (val & 0x00FF) << 8 | (val & 0xFF00) >> 8;
}

static uint32_t le32(uint32_t val) 
{
    return (val & 0x000000FF) << 24 |
           (val & 0x0000FF00) << 8  |
           (val & 0x00FF0000) >> 8  |
           (val & 0xFF000000) >> 24;
}

// 初始化Ogg流
static void ogg_init(OggState *ogg) 
{
    memset(&ogg->op, 0, sizeof(ogg_packet));
    memset(&ogg->og, 0, sizeof(ogg_page));
    ogg->op.packet = NULL;
    ogg->op.bytes = 0;
    ogg->op.b_o_s = 0;
    ogg->op.e_o_s = 0;
    ogg->op.granulepos = 0;
    ogg->op.packetno = 0;

    int serial = rand() % 0x7FFFFFFF + 1;
    if (ogg_stream_init(&ogg->os, serial) != 0) 
    {
        fprintf(stderr, "错误:Ogg流初始化失败\n");
        exit(EXIT_FAILURE);
    }
}

// 释放Ogg资源
static void ogg_destroy(OggState *ogg) 
{
    ogg->op.packet = NULL;
    ogg_packet_clear(&ogg->op);
    ogg_stream_clear(&ogg->os);
}

// 写入OpusHead头（强制flush）
static int write_opus_head(FILE *out, OggState *ogg) 
{
    OpusHead head;
    memcpy(head.magic, "OpusHead", 8);
    head.version = 1;
    head.channels = CHANNELS;
    head.pre_skip = le16(0);
    head.sample_rate = le32(SAMPLE_RATE);
    head.gain = le16(0);
    head.channel_map = 0;

    ogg->op.packet = (unsigned char*)&head;
    ogg->op.bytes = sizeof(OpusHead);
    ogg->op.b_o_s = 1;
    ogg->op.granulepos = 0;
    ogg->op.packetno = 0;
    ogg_stream_packetin(&ogg->os, &ogg->op);

    if (ogg_stream_flush(&ogg->os, &ogg->og) > 0) 
    {
        fwrite(ogg->og.header, 1, ogg->og.header_len, out);
        fwrite(ogg->og.body, 1, ogg->og.body_len, out);
        ogg->op.packet = NULL;
        ogg->op.bytes = 0;
        ogg->op.b_o_s = 0;
        return 0;
    }
    fprintf(stderr, "错误:OpusHead写入失败\n");
    return -1;
}

// 写入OpusTag头（强制flush）
static int write_opus_tag(FILE *out, OggState *ogg) 
{
    OpusTag tag;
    memcpy(tag.magic, "OpusTags", 8);
    tag.vendor_len = le32(0);

    ogg->op.packet = (unsigned char*)&tag;
    ogg->op.bytes = sizeof(OpusTag);
    ogg->op.granulepos = 0;
    ogg->op.packetno = 1;
    ogg_stream_packetin(&ogg->os, &ogg->op);

    if (ogg_stream_flush(&ogg->os, &ogg->og) > 0) 
    {
        fwrite(ogg->og.header, 1, ogg->og.header_len, out);
        fwrite(ogg->og.body, 1, ogg->og.body_len, out);
        ogg->op.packet = NULL;
        ogg->op.bytes = 0;
        return 0;
    }
    fprintf(stderr, "错误:OpusTag写入失败\n");
    return -1;
}

// 生成连续相位正弦波（避免编码断层）
static void generate_sine(short *pcm) 
{
    static double phase = 0.0;  // 静态相位确保帧间连续
    double phase_step = 2 * M_PI * FREQUENCY / SAMPLE_RATE;

    for (int i = 0; i < FRAME_SIZE; i++) 
    {
        pcm[i] = (short)(AMPLITUDE * sin(phase));
        phase += phase_step;
        if (phase > 2 * M_PI) phase -= 2 * M_PI;  // 相位归一化
    }
}

// 初始化Opus编码器
static OpusEncoder* init_opus(int *err) 
{
    OpusEncoder *encoder = opus_encoder_create(
        SAMPLE_RATE,
        CHANNELS,
        OPUS_APPLICATION_AUDIO,
        err
    );
    if (*err != OPUS_OK || !encoder) return NULL;

    // 关键配置:禁用动态特性，确保帧稳定（解决中途播放失败）
    opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));       // 固定比特率
    opus_encoder_ctl(encoder, OPUS_SET_VBR(0));                 // 禁用VBR
    opus_encoder_ctl(encoder, OPUS_SET_DTX(0));                 // 禁用静音丢帧
    opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(5));          // 中等复杂度
    opus_encoder_ctl(encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));// 兼容旧版（替换SINE）
    opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(0));    // 无丢包（避免FEC）

    return encoder;
}

// 编码PCM（校验帧有效性，避免无效数据）
static int encode_pcm(OpusEncoder *encoder, const short *pcm, unsigned char *opus_buf) {
    int opus_len = opus_encode(
        encoder,
        pcm,
        FRAME_SIZE,
        opus_buf,
        MAX_OPUS_FRAME
    );

    // 校验帧长度（32kbps*20ms=640字节，允许±10%波动）
    if (opus_len < 32 || opus_len > 704) {
        fprintf(stderr, "警告:无效帧长度（%d字节），跳过\n", opus_len);
        return -1;
    }
    return opus_len;
}

// 写入音频包（时间戳+序号严格连续）
static void write_audio_packet(FILE *out, OggState *ogg, unsigned char *buf, int len, 
                              ogg_int64_t granulepos, int packetno) {
    ogg->op.packet = buf;
    ogg->op.bytes = len;
    ogg->op.granulepos = granulepos;  // 累计采样数（严格递增）
    ogg->op.packetno = packetno;      // 包序号（严格递增）
    ogg->op.e_o_s = 0;
    ogg_stream_packetin(&ogg->os, &ogg->op);

    // 及时输出Ogg页
    while (ogg_stream_pageout(&ogg->os, &ogg->og) > 0) 
    {
        fwrite(ogg->og.header, 1, ogg->og.header_len, out);
        fwrite(ogg->og.body, 1, ogg->og.body_len, out);
    }
    ogg->op.packet = NULL;
}

// 写入流结束标记
static void write_eos(FILE *out, OggState *ogg, ogg_int64_t granulepos, int packetno) 
{
    ogg->op.packet = NULL;
    ogg->op.bytes = 0;
    ogg->op.granulepos = granulepos;
    ogg->op.packetno = packetno;
    ogg->op.e_o_s = 1;
    ogg_stream_packetin(&ogg->os, &ogg->op);

    while (ogg_stream_flush(&ogg->os, &ogg->og) > 0) 
    {
        fwrite(ogg->og.header, 1, ogg->og.header_len, out);
        fwrite(ogg->og.body, 1, ogg->og.body_len, out);
    }
}

int generateSinOpus(const char *output_file)
{
    printf("generateSinOpus() 输出文件: %s\n", output_file);
    FILE *fp = fopen(output_file, "wb");
    if (!fp) 
    { 
        perror("错误:无法打开文件"); 
        return 1; 
    }

    int total_frames = TOTAL_SAMPLES / FRAME_SIZE;
    printf("=== 生成1KHz正弦波Opus文件 ===\n");
    printf("配置:%d秒 | %dHz | %d声道 | %dkbps | 20ms固定帧长\n",
           DURATION, SAMPLE_RATE, CHANNELS, BITRATE/1000);
    printf("输出:%s\n", output_file);

    // 初始化Ogg
    OggState ogg;
    srand((unsigned int)time(NULL));
    ogg_init(&ogg);

    // 初始化Opus编码器
    int opus_err;
    OpusEncoder *encoder = init_opus(&opus_err);
    if (!encoder) 
    {
        fprintf(stderr, "错误:Opus初始化失败:%s\n", opus_strerror(opus_err));
        ogg_destroy(&ogg);
        fclose(fp);
        return 1;
    }

    // 写入Opus头 "OpusHead"之类的
    if (write_opus_head(fp, &ogg) != 0 || write_opus_tag(fp, &ogg) != 0) 
    {
        fprintf(stderr, "错误:头写入失败\n");
        return 1;
    } 

    // 分配缓冲区
    short *pcm_buf = (short*)malloc(FRAME_SIZE * sizeof(short));
    unsigned char *opus_buf = (unsigned char*)malloc(MAX_OPUS_FRAME);
    if (!pcm_buf || !opus_buf) 
    { 
        perror("错误:内存不足"); 
        return 1; 
    }

    // 主循环:时间戳+序号严格连续（核心修复）
    ogg_int64_t granulepos = 0;  // 每帧固定+960
    int current_packetno = 2;     // 从2开始（0=Head，1=Tag）
    for (int frame_idx = 0; frame_idx < total_frames; frame_idx++) 
    {
        // 生成PCM
        generate_sine(pcm_buf);

        // 编码
        int opus_len = encode_pcm(encoder, pcm_buf, opus_buf);

        // 时间戳必须递增（即使编码失败）
        granulepos += FRAME_SIZE;

        // 写入有效帧
        if (opus_len > 0) 
        {
            write_audio_packet(fp, &ogg, opus_buf, opus_len, granulepos, current_packetno);
        }

        // 序号必须递增（即使编码失败）
        current_packetno++;

        // 进度
        if (frame_idx % 10 == 0) 
        {
            printf("进度:%.1f%%\r", (float)frame_idx / total_frames * 100);
            fflush(stdout);
        }
    }
    printf("进度:100.0%%\n");

    // 写入结束标记
    write_eos(fp, &ogg, granulepos, current_packetno);

    // 强制刷盘
    fflush(fp);

    // 释放资源
    free(pcm_buf);
    free(opus_buf);
    opus_encoder_destroy(encoder);
    ogg_destroy(&ogg);
    fclose(fp);

    printf("=== 完成！ ===\n");
    printf("播放:ffplay %s\n", output_file);
    return 0;
}


